package gopass

import (
  "appengine"
  "appengine/user"
  "net/http"
  "time"
  "appengine/datastore"
  "html/template"
  "encoding/json"
  "fmt"
  "strings"
  "math/rand"
  "strconv"
  "crypto/aes"
//  "io"
//  "crypto/sha256"
)


type Node struct {
  // Plain text
  Keyword string
  Create time.Time
  RandomID int
  // Encrypted
  Pass []byte
}

var templates = template.Must(
  template.ParseFiles("hello/template/main.html", "hello/template/pass.html"))

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func init() {
  rand.Seed(time.Now().UnixNano())
  http.HandleFunc("/", root)
  http.HandleFunc("/go", ajaxGo)
  http.HandleFunc("/get", getPass)
  http.HandleFunc("/del", delPass)
}

const(
  alphabet = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
)

func genPass(maxLen int) []byte {
  password := make([]byte, maxLen)
	for i := 0; i < len(password); i++ {
    password[i] = alphabet[rand.Int() % len(alphabet)]
  }
  return password
}

type AjaxRet struct {
  RandomID int
  Keyword string
  Date string
  Note  string
}

func getPass(w http.ResponseWriter, r *http.Request) {
  c := appengine.NewContext(r)
  existID := r.FormValue("existID")
  masterSha := r.FormValue("masterpassword")
  masterShaByte := make([]byte, len(masterSha) / 2)
  for i := 0; i < len(masterSha) / 2; i++ {
    x, _ := strconv.Atoi(masterSha[i * 2 : i * 2 + 1])
    masterShaByte[i] = byte(x)
  }
  aes1, err := aes.NewCipher(masterShaByte)
  check(err)

  maxLen, err := strconv.ParseInt(r.FormValue("maxLen"), 10, 32)
  if err != nil {
    maxLen = 16
  }

  if (len(existID) == 0) {
    password := genPass(int(maxLen))
    keywords := r.FormValue("ajaxSearch")

    // AES
    out := make([]byte, len(password))
    aes1.Encrypt(out, password)

    n1 := Node {
      RandomID: rand.Int(),
      Keyword:  keywords,
      Create:   time.Now(),
      Pass: out,
    }
    _, err = datastore.Put(c, datastore.NewIncompleteKey(c, "Node", nil), &n1)
    check(err)

    err = templates.ExecuteTemplate(w, "pass.html", 
      AjaxRet{n1.RandomID, n1.Keyword, n1.Create.Format(time.ANSIC), string(password)})
    check(err)
    return
  } else {
    // get password
    id, err := strconv.Atoi(existID)
    check(err)

    q := datastore.NewQuery("Node").Filter("RandomID =", id)
    t := q.Run(c)
    var e Node
    _, err = t.Next(&e)
    check(err)

    plain := make([]byte, len(e.Pass))
    aes1.Decrypt(plain, e.Pass)

    err = templates.ExecuteTemplate(w, "pass.html", 
      AjaxRet{e.RandomID, e.Keyword, e.Create.Format(time.ANSIC), string(plain)});
    check(err)
    return
  }
}

func delPass(w http.ResponseWriter, r *http.Request) {
  c := appengine.NewContext(r)
  RandomID := r.FormValue("RandomID")
  id, err := strconv.Atoi(RandomID)
  q := datastore.NewQuery("Node").Filter("RandomID =", id)
  t := q.Run(c)
  var e Node
  key, err := t.Next(&e)
  check(err)
  err = datastore.Delete(c, key)
  check(err)
  w.Header().Set("Location", "/")
  w.WriteHeader(http.StatusFound)
}

func haveAll(s string, q []string) bool {
  for i := 0; i < len(q); i++ {
    if strings.Contains(s, q[i]) == false {
      return false
    }
  }
  return true
}

func ajaxGo(w http.ResponseWriter, r *http.Request) {
  c := appengine.NewContext(r)
  text := r.FormValue("text")
  qt := strings.Split(text, " ")

  q := datastore.NewQuery("Node").Limit(20)
  n1 := make([]Node, 0, 20)
  _, err := q.GetAll(c, &n1)
  check(err)
  ret := make([]AjaxRet, 0, len(n1))
  for i := 0; i < len(n1); i++ {
    if haveAll(n1[i].Keyword, qt) {
      ret = append(ret, AjaxRet{
        n1[i].RandomID, n1[i].Keyword, n1[i].Create.Format(time.ANSIC), "Notes"})
    }
  }
  r1, err := json.Marshal(ret)
  check(err)

	jsonType := "application/json"
	if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
		w.Header().Set("Content-Type", jsonType)
	}
	fmt.Fprintln(w, string(r1))
}

func root(w http.ResponseWriter, r *http.Request) {
  c := appengine.NewContext(r)
  u := user.Current(c)
  if u == nil {
    url, err := user.LoginURL(c, r.URL.String())
    if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
    }
    w.Header().Set("Location", url)
    w.WriteHeader(http.StatusFound)
    return
  }

  if err := templates.ExecuteTemplate(w, "main.html", nil); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}


